﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Microsoft.SharePoint;
using SharePoint.Orm.Helpers;

namespace SharePoint.Orm.SiteBuilders {
	/// <summary>
	/// Provides the ability to find/add fields.
	/// </summary>
	public class FieldBuilder : BuilderBase<SPField> {
		/// <summary>
		/// Required.  The name of the field.
		/// </summary>
		public string Name { get; set; }

		/// <summary>
		/// Required.  The string representation of a unique identifier (with or without braces).  Tip: If you have Resharper 
		/// type nguid + tab to generate this value.
		/// </summary>
		public string GuidStr { get; set; }

		/// <summary>
		/// Optional, default = true, but totally worth being explicit about.
		/// </summary>
		public bool Required { get; set; }

		/// <summary>
		/// Optional, default = null.  If set this will be displayed to the end user as a description under the field name in add 
		/// and edit forms.  If you leave it null for DateTime's it will display 'mm/dd/yyyy', you can override by setting to "".
		/// </summary>
		public string Description { get; set; }

		/// <summary>
		/// Optional, default = false.  Will hide the field in add, edit, view, display pages.  You can still explicitly add it to views 
		/// though if you want.
		/// </summary>
		public bool Hidden { get; set; }

		/// <summary>
		/// Optional, default = true.  This will disable the user from editing anything other than the display name of the field.
		/// </summary>
		public bool Sealed { get; set; }

		/// <summary>
		/// Optional, default SPFieldType.Text.  This is mutually exclusive with the FieldTypeString property, you should only use one.
		/// </summary>
		public SPFieldType FieldType { get; set; }

		/// <summary>
		/// Optional.  This should rarely be used, only for custom field types.  Typically you use the FieldType property.  If you do set this field the FieldType property will be ignored.
		/// </summary>
		public string FieldTypeString { get; set; }

		/// <summary>
		/// Required.  The group description to add the field type to.  This is static so you can set it once and instantiate 
		/// the builder for each field.
		/// </summary>
		public static string GroupName { get; set; }

		/// <summary>
		/// Optional, default = true.
		/// </summary>
		public bool ShowInDisplayForm { get; set; }

		/// <summary>
		/// Optional, default = false.
		/// </summary>
		public bool ShowInViewForms { get; set; }

		/// <summary>
		/// Optional, default = true.
		/// </summary>
		public bool ShowInVersionHistory { get; set; }

		/// <summary>
		/// Optional, default = true.
		/// </summary>
		public bool ShowInNewForm { get; set; }

		/// <summary>
		/// Optional, default = true.
		/// </summary>
		public bool ShowInListSettings { get; set; }

		/// <summary>
		/// Optional, default = true.
		/// </summary>
		public bool ShowInEditForm { get; set; }

		/// <summary>
		/// Optional, default = Name.  The name of the field as a user would see it.
		/// </summary>
		public string DisplayName { get; set; }

		/// <summary>
		/// Optional, default = SPDateTimeFieldFormatType.DateOnly.  This is only used when FieldType = SPFieldType.DateTime.
		/// </summary>
		public SPDateTimeFieldFormatType DateTimeFormat { get; set; }

		/// <summary>
		/// Optional, default = SPChoiceFormatType.Dropdown.  This is only relevant when FieldType = SPFieldType.Choice.
		/// </summary>
		public SPChoiceFormatType EditFormat { get; set; }

		/// <summary>
		/// Optional, default = Empty.  This is only relevant when FieldType = SPFieldType.Choice.
		/// </summary>
		public IEnumerable<string> Choices { get; set; }
		/// <summary>
		/// Optional, default = Empty. This is relevant for many fields.
		/// </summary>
		public string DefaultValue { get; set; }
        /// <summary>
        /// Optional, default = false. When set, all of the ShowInXXX become false. This is useful if you have a field that is filtered upon but do not want it to show anywhere.
        /// </summary>
        public bool ShowNoWhere { get; set; }
		/// <summary>
		/// Initializes default properties of fields (see individual field descriptions).
		/// </summary>
		public FieldBuilder() {
			Hidden = false;
			Sealed = true;
			Required = true;
			FieldType = SPFieldType.Text;
			ShowInDisplayForm = true;
			ShowInEditForm = true;
			ShowInListSettings = true;
			ShowInNewForm = true;
			ShowInVersionHistory = true;
			ShowInViewForms = true;
			DateTimeFormat = SPDateTimeFieldFormatType.DateOnly;
			Choices = new List<string>();
			DefaultValue = string.Empty;
			EditFormat = SPChoiceFormatType.Dropdown;
		    ShowNoWhere = false;
		}

		public override SPField FindAdd() {
			if (Site == null) throw new SPOrmException("site can not be null.");
			if (string.IsNullOrEmpty(Name)) throw new SPOrmException("Name can not be empty.");
			if (string.IsNullOrEmpty(GuidStr)) throw new SPOrmException("GuidStr can not be empty.");
			if (string.IsNullOrEmpty(GroupName)) throw new SPOrmException("GroupName can not be empty.");

			if (string.IsNullOrEmpty(DisplayName)) {
				DisplayName = Name;
				Name = Regex.Replace(Name, @"[^a-zA-Z0-9]", "");
			}

			SPField field = FindOrAddField();

			field.Title = DisplayName;
			field.Description = Description;
			field.Hidden = Hidden;
			field.Sealed = Sealed;
			field.Required = Required;
			field.DefaultValue = DefaultValue;
		    if (ShowNoWhere) {
		        field.ShowInEditForm = false;
		        field.ShowInDisplayForm = false;
		        field.ShowInListSettings = false;
		        field.ShowInNewForm = false;
		        field.ShowInVersionHistory = false;
		        field.ShowInViewForms = false;
		    }
		    else {
		        field.ShowInEditForm = ShowInEditForm;
		        field.ShowInDisplayForm = ShowInDisplayForm;
		        field.ShowInListSettings = ShowInListSettings;
		        field.ShowInNewForm = ShowInNewForm;
		        field.ShowInVersionHistory = ShowInVersionHistory;
		        field.ShowInViewForms = ShowInViewForms;
		    }

		    if (FieldType == SPFieldType.DateTime) {
				SPFieldDateTime dateTimeField = (SPFieldDateTime)field;
				dateTimeField.DisplayFormat = DateTimeFormat;
				if (Description == null) {
					dateTimeField.Description = "mm/dd/yyyy";
				}
			}

			if (FieldType == SPFieldType.Choice || FieldType == SPFieldType.MultiChoice) {
				SPFieldMultiChoice multiChoice = (SPFieldMultiChoice)field;

				// SPFieldChoice inherits from SPFieldMultiChoice
				SPFieldChoice fieldChoice = field as SPFieldChoice;
				if (fieldChoice != null) {
					fieldChoice.EditFormat = EditFormat;
				}

				foreach (string choice in Choices) {
					string choiceToFindAdd = choice;
					bool listAlreadyContainsChoice = multiChoice.Choices.Cast<string>().Any(c => c == choiceToFindAdd);
					if (!listAlreadyContainsChoice) {
						multiChoice.Choices.Add(choice);
					}
				}
			}

			field.Update(true);
			return field;
		}

		private SPField FindOrAddField() {
			if (FieldType == SPFieldType.User)
				return SPWebHelper.FindOrAddFieldUser(Site, new Guid(GuidStr), GroupName, Name, Required);
			if (string.IsNullOrEmpty(FieldTypeString))
				return SPWebHelper.FindOrAddField(Site, new Guid(GuidStr), GroupName, Name, FieldType, Required);
			return SPWebHelper.FindOrAddField(Site, new Guid(GuidStr), GroupName, Name, FieldTypeString);
		}
	}
}
